{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "raw_mimetype": "text/restructuredtext"
   },
   "source": [
    ".. _nb_interface_problem:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Problem\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "There exist a couple of different ways for defining an optimization problem in *pymoo*. In contrast to other optimization frameworks in Python, the preferred way is to define an **object**. However, a problem can also be defined by functions as shown [here](../problems/index.ipynb). Most algorithms in *pymoo* are population-based, which implies in each generation, not a single but multiple solutions are evaluated. Thus, the problem implementation retrieves the set of solutions to provide the most flexibility to the end-user. This flexibility allows you to implement a custom parallelization and thus to use your hardware most efficiently. Three different ways of defining a problem are shown below:"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {
    "raw_mimetype": "text/restructuredtext"
   },
   "source": [
    ".. admonition:: Overview\n",
    "    :class: myOwnStyle\n",
    "\n",
    "    - `Problem <../problems/index.ipynb>`_: Object-oriented definition :code:`Problem` which implements a method evaluating a **set** of solutions.\n",
    "    - `ElementwiseProblem <../problems/index.ipynb>`_: Object-oriented definition :code:`ElementwiseProblem` which implements a function evaluating a **single** solution at a time. \n",
    "    - `FunctionalProblem <../problems/index.ipynb>`_: Define a problem :code:`FunctionalProblem` by using a **function** for each objective and constraint."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next, we define an **unconstrained** optimization problem with two variables and two objectives. Because the lower and upper bounds are identical for both variables, only a float value is passed to the `Problem` constructor. Assuming the `Algorithm` has a population size N, the input variable `x` is a two-dimensional matrix with the dimensions (N,2). The input has two columns because the optimization problem has `n_var=2`. Thus, to evaluated the problem makes use of the vectorized calculations `[:, 0]` and `[:, 1]` to select the first and second variables for each row in the input matrix `x`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "from pymoo.core.problem import Problem\n",
    "\n",
    "class MyProblem(Problem):\n",
    "\n",
    "    def __init__(self):\n",
    "        super().__init__(n_var=2, \n",
    "                         n_obj=2, \n",
    "                         xl=-2.0, \n",
    "                         xu=2.0)\n",
    "\n",
    "    def _evaluate(self, x, out, *args, **kwargs):\n",
    "        f1 = 100 * (x[:, 0]**2 + x[:, 1]**2)\n",
    "        f2 = (x[:, 0]-1)**2 + x[:, 1]**2\n",
    "        out[\"F\"] = np.column_stack([f1, f2])\n",
    " "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Below we define a **constrained** optimization problem with two variables and two objectives. Here, the problem is defined **element-wise**. The lower and upper bounds, `xl` and `xu`, are defined using a vector with a length equal to the number of variables. The input `x` is a **one-dimensional** array of length two and is called N times in each iteration for the algorithm discussed above."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "from pymoo.core.problem import ElementwiseProblem\n",
    "\n",
    "class MyProblem(ElementwiseProblem):\n",
    "\n",
    "    def __init__(self):\n",
    "        super().__init__(n_var=2, \n",
    "                         n_obj=2, \n",
    "                         n_ieq_constr=2, \n",
    "                         xl=np.array([-2,-2]), \n",
    "                         xu=np.array([2,2]))\n",
    "\n",
    "    def _evaluate(self, x, out, *args, **kwargs):\n",
    "        f1 = 100 * (x[0]**2 + x[1]**2)\n",
    "        f2 = (x[0]-1)**2 + x[1]**2\n",
    "        \n",
    "        g1 = 2*(x[0]-0.1) * (x[0]-0.9) / 0.18\n",
    "        g2 = - 20*(x[0]-0.4) * (x[0]-0.6) / 4.8\n",
    "        \n",
    "        out[\"F\"] = [f1, f2]\n",
    "        out[\"G\"] = [g1, g2]\n",
    "        \n",
    "\n",
    "problem = MyProblem()"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {
    "raw_mimetype": "text/restructuredtext"
   },
   "source": [
    ".. admonition:: Tip\n",
    "    :class: myOwnStyle\n",
    "\n",
    "    For more information, please look at the problem `tutorial <../problems/index.ipynb>`_. Moreover, a number of test problems frequently being use for benchmarking the performance of an algorithm are listed `here <../problems/test_problems.ipynb>`_."
   ]
  }
 ],
 "metadata": {
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
